﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System;
using ScrumTable.Common;
using ScrumTable.Common.Data.Tracking;

#endregion


namespace ScrumTable.DL.Data.Generic.Cache
{
    public enum CacheElementListEntryState
    {
        /// <summary>
        /// The element has not been modified.
        /// </summary>
        Unmodified = 0,

        /// <summary>
        /// The element is new and should be saved back to tfs.
        /// </summary>
        New,

        /// <summary>
        /// The element value has been removed. This change should be
        /// saved back to tfs.
        /// </summary>
        Removed
    }

    /// <summary>
    /// Represents a state container for list element entry. The State property
    /// is for error handling and synchronization purposes. If a server side
    /// connection error occurs the Bucket is used to restore the state before
    /// the ExportChanges() call.
    /// </summary>
    /// <typeparam name="T">Specifies the type of the cache element.</typeparam>
    public class CacheElementListEntryBucket<T> : ICloneable
        where T : class, ICacheElement
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        private CacheElementListEntryState _state;

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets or sets the associated element, which is described by the current
        /// bucket.
        /// </summary>
        public T Element { get; set; }

        /// <summary>
        /// Gets the current bucket state.
        /// </summary>
        public CacheElementListEntryState State
        {
            get { return _state; }
            set
            {
                if (!Equals(_state, value) || RevManager.IsInitial)
                {
                    _state = value;
                    RevManager.Next();
                }
            }
        }

        /// <summary>
        /// Gets the original bucket state.
        /// </summary>
        public CacheElementListEntryState OriginalState { get; set; }

        /// <summary>
        /// Returns true if the current element has changes. This property
        /// is independent from the elements children.
        /// </summary>
        public bool CurrentIsDirty
        {
            get { return State != CacheElementListEntryState.Unmodified; }
        }

        /// <summary>
        /// Gets the internal cache revision of the State property.
        /// </summary>
        public double CacheRevision
        {
            get { return RevManager.Current; }
        }

        /// <summary>
        /// Returns true, if the current bucket or its child element is dirty.
        /// </summary>
        public bool IsDirty
        {
            get { return (CurrentIsDirty || Element.IsDirty); }
        }

        private RevisionManager RevManager { get; set; }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new CacheElementListEntryBucket instance.
        /// </summary>
        public CacheElementListEntryBucket()
        {
            RevManager = new RevisionManager();
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        object ICloneable.Clone()
        {
            return Clone();
        }

        /// <summary>
        /// Merges the current element with the given cache bucket.
        /// </summary>
        /// <param name="toMerge">Specifies the bucket to merge and import.</param>
        /// <param name="saveProgressFeedback">Specifies the save feedback from the underlying system.</param>
        internal void MergeChanges(CacheElementListEntryBucket<T> toMerge, ICacheSaveProgressFeedback saveProgressFeedback)
        {
            PreCondition.AssertNotNull(toMerge, "toMerge");
            PreCondition.AssertNotNull(saveProgressFeedback, "saveProgressFeedback");

            switch (saveProgressFeedback.State)
            {
                case CacheSavePairState.Saved:
                    OnMergeChangeSaved(toMerge, saveProgressFeedback);
                    break;
                case CacheSavePairState.Deleted:
                    OnMergeChangeDeleted(toMerge, saveProgressFeedback);
                    break;
                case CacheSavePairState.Updated:
                    OnMergeChangeInvalidateData(toMerge, saveProgressFeedback);
                    break;
                case CacheSavePairState.Error:
                    OnMergeChangeError(toMerge, saveProgressFeedback);
                    break;
                default: throw new ArgumentOutOfRangeException();
            }
        }

        /// <summary>
        /// Removes the dirty flag on the current element.
        /// </summary>
        internal void RemoveDirtyFlag()
        {
            _state = CacheElementListEntryState.Unmodified;
            OriginalState = CacheElementListEntryState.Unmodified;
        }

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        internal CacheElementListEntryBucket<T> Clone()
        {
            return new CacheElementListEntryBucket<T>
            {
                _state = State,
                OriginalState = OriginalState,
                Element = (T)Element.Clone(),
                RevManager = RevManager.Clone()
            };
        }


        private void ImportFailedChanges(CacheElementListEntryBucket<T> toMerge, ICacheSaveProgressFeedback saveProgressFeedback)
        {
            ImportElement(toMerge, saveProgressFeedback);

            _state = toMerge.State;
            OriginalState = toMerge.OriginalState;
        }

        private void ImportElement(CacheElementListEntryBucket<T> toMerge, ICacheSaveProgressFeedback saveProgressFeedback)
        {
            if (Element == null)
                Element = toMerge.Element;
            else
                Element.MergeChanges(toMerge.Element, saveProgressFeedback);
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        /// <summary>
        /// An error occurred while saving the given cache element. This means that
        /// the given element must be merged back (if there are no changes) into
        /// the cache tree.
        /// </summary>
        /// <param name="toMerge">Specifies the cache element which caused the error.</param>
        /// <param name="saveProgressFeedback">Specifies the save feedback provided by the data driver.</param>
        protected virtual void OnMergeChangeError(CacheElementListEntryBucket<T> toMerge, ICacheSaveProgressFeedback saveProgressFeedback)
        {
            ImportFailedChanges(toMerge, saveProgressFeedback);
        }

        /// <summary>
        /// An element on server side has changed (which behaviour may caused an error). Its
        /// cache data must be invalidated in order to resynchronize with the server.
        /// </summary>
        /// <param name="toMerge">Specifies the cache element which data should be invalidated.</param>
        /// <param name="saveProgressFeedback">Specifies the save feedback provided by the data driver.</param>
        protected virtual void OnMergeChangeInvalidateData(CacheElementListEntryBucket<T> toMerge, ICacheSaveProgressFeedback saveProgressFeedback)
        {
            ImportElement(toMerge, saveProgressFeedback);
        }

        /// <summary>
        /// An element has saved on server side. This means that data in the local storage may
        /// be invalidated if the change caused additional changes on server side. Workflow
        /// changes may cause additional value changes.
        /// </summary>
        /// <param name="toMerge">Specifies the cache element which data have ben saved.</param>
        /// <param name="saveProgressFeedback">Specifies the save feedback provided by the data driver.</param>
        protected virtual void OnMergeChangeSaved(CacheElementListEntryBucket<T> toMerge, ICacheSaveProgressFeedback saveProgressFeedback)
        {
            ImportElement(toMerge, saveProgressFeedback);
        }

        /// <summary>
        /// An element on server side has been deleted. The element in the cache must also be 
        /// deleted.
        /// </summary>
        /// <param name="toMerge">Specifies the cache element which has been deleted.</param>
        /// <param name="saveProgressFeedback">Specifies the save feedback provided by the data driver.</param>
        protected virtual void OnMergeChangeDeleted(CacheElementListEntryBucket<T> toMerge, ICacheSaveProgressFeedback saveProgressFeedback)
        {
            ImportElement(toMerge, saveProgressFeedback);
        }

        #endregion
    }
}